package com.qf.fc.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qf.fc.Param.UserDto;
import com.qf.fc.Param.UserPass;
import com.qf.fc.dao.UserDao;
import com.qf.fc.dao.UserDetailDao;
import com.qf.fc.dao.UserLogDao;
import com.qf.fc.entity.User;
import com.qf.fc.entity.UserDetail;
import com.qf.fc.entity.UserLog;
import com.qf.fc.service.UserService;
import com.qf.fc.utils.JwtUtils;
import com.qf.fc.vo.R;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import com.qf.fc.utils.RegexUtils;

import java.time.Duration;
import java.util.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;

/**
 * 用户表(User)表服务实现类
 *
 * @author makejava
 * @since 2024-09-16 22:56:03
 */
@Service("userService")
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements UserService {

    private final UserDao dao;

    private final UserDetailDao detailDao;

    private final UserLogDao logDao;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public R sentCode(String phone, HttpSession session) {
        // 校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            return R.fail("手机号无效！请重新输入");
        }

        // 生成随机6位验证码
        String code = RandomUtil.randomNumbers(6);

        // 将验证码存储到 Redis，设置过期时间为2分钟
        stringRedisTemplate.opsForValue().set("pz:login:code:" + phone, code, Duration.ofMinutes(2));

        // 模拟发送验证码
        log.info("验证码已发送：{}", code);

        // 返回成功
        return R.ok("验证码发送成功");
    }

    private boolean queryPhone(String phone){
        //1.构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhone,phone);
        //2.查询数据库
        return dao.selectOne(wrapper)==null;
    }
    @Override
    public R<String> checkPhone(String phone) {
        if (queryPhone(phone)){
            //手机号不存在
            return  R.ok("可以用");
        }
        return R.fail("亲，手机号已存在！");
    }

    @Override
    @Transactional
    public R<String> register(UserDto dto) {
        //校验 省略
        if (dto!=null){
            //查询手机号 是否可以注册
            if (queryPhone(dto.getPhone())){
                // 创建对象,密码需要密文
                User user = new User(dto.getPhone(), SecureUtil.sha1(SecureUtil.md5(dto.getPass())));
                // 操作数据库
                if (dao.insert(user)>0){
                    logDao.insert(new UserLog(user.getId(), "注册", "实现了新用户注册"));
                    //个人详情初始化
                    detailDao.insert(new UserDetail(user.getId()));
                    return R.ok("亲，恭喜你注册成功！");
                }
            }
        }
        return R.fail("亲，很遗憾注册失败！");
    }

    @Override
    public R login(UserDto dto, HttpSession session) {
        //登录校验
        if(!StrUtil.isNotBlank(dto.getPhone()) || !StrUtil.isNotBlank(dto.getPass())){
            return R.fail("亲，请检查参数(请输入账号或密码)！");
        }
        // 校验一下手机号和验证码
        if (RegexUtils.isPhoneInvalid(dto.getPhone())){
            return R.fail("手机号无效！请重新输入");
        };
        // 从redis中获取验证码进行校验
        String code = stringRedisTemplate.opsForValue().get("pz:login:code:" + dto.getPhone());
        // 不一样返回错误
        if (code == null || !Objects.equals(code, dto.getCode())){
            return R.fail("请输入正确验证码！");
        } 
        // 查询用户
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhone, dto.getPhone());
        wrapper.eq(User::getPassword, SecureUtil.sha1(SecureUtil.md5(dto.getPass())));
        User user = dao.selectOne(wrapper);
        //校验是否存在
        if (user==null){
            return R.fail("亲，账号或密码错误！");
        }
        //记录日志
        logDao.insert(new UserLog(user.getId(), "登录", "用户登录成功！"));

        Map<String,Object> claims = new HashMap<>();
        claims.put("phone",dto.getPhone());
        claims.put("password",dto.getPass());
        //如果登陆成功，生成令牌，下发令牌
        String jwt = JwtUtils.generateJwt(claims);
        //登录失败，返回错误信息

//        登陆成功 前后端分离 令牌作为前后端分离标识
        String token = SecureUtil.sha1(SecureUtil.md5(user.getId()+"-"+user.getPhone()));
        // 查询用户详情
        UserDetail detail = detailDao.selectOne(new LambdaQueryWrapper<UserDetail>().eq(UserDetail::getUid,user.getId()));
        // 准备 返回的数据
        Map map = new HashMap();
        map.put("token",jwt);
        map.put("detail",detail);
        user.setPassword("保密");
        map.put("user",user);
        return R.ok(map);
    }

    @Override
    public R<String> logout(String token) {
        return R.ok("");
    }

    @Override
    public R<String> findPass(UserDto dto) {
        //校验前端传过来的数据，是否满足要求
        if (dto==null || !StringUtils.hasLength(dto.getPhone()) || dto.getPass().isEmpty()){
            return R.fail("亲，请检查参数！");
        }
        if (Objects.equals(SecureUtil.sha1(SecureUtil.md5(dto.getPass())), dao.findPass(dto.getPhone()))) {
            return R.fail("亲，你设置的密码冲突！");
        } else{
            //2.修改密码
            if(dao.updatePass(SecureUtil.sha1(SecureUtil.md5(dto.getPass())), dto.getPhone())){
                return R.ok("亲，修改密码成功！");
            }
        }
        return R.fail("亲，数据操作失败，请检查网络！");
    }

    @Override
    public R<String> updatePass(UserPass pass) {
        // 组装对象
        User user = new User();
        user.setId(pass.getUid());
        user.setPassword(SecureUtil.sha1(SecureUtil.md5(pass.getPass())));
        user.setUpdateTime(new Date());
        //实现数据库修改
        if (dao.updateById(user)>0){
            return R.ok("修改密码成功！");
        }
        return R.fail("亲，修改失败，请检查！");
    }

    @Override
    public R<List<User>> queryByPhone(String phone) {
        // 创建条件构造器
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        if (StringUtils.hasLength(phone)){
            wrapper.eq(User::getPhone,phone);
        }
        wrapper.orderByDesc(User::getCreateTime);
        // 查询返回
        return R.ok(dao.selectList(wrapper));
    }
}

